/** 常用工具方法 license by http://eleadmin.com */

/**
 * 数字前置补零
 * @param value 值
 * @param length 位数
 * @returns {string}
 */
export function digit(value, length = 2) {
  if (
    typeof value === 'undefined' ||
    value === null ||
    String(value).length >= length
  ) {
    return value;
  }
  return (Array(length).join('0') + value).slice(-length);
}

/**
 * 时间格式化
 * @param time 时间
 * @param format 格式
 * @returns {string}
 */
export function toDateString(time, format = 'yyyy-MM-dd HH:mm:ss') {
  if (!time) {
    return '';
  }
  if (typeof time === 'number' && String(time).length === 10) {
    time = time * 1000; // 10位时间戳处理
  }
  const date = new Date(time);
  const ymd = [
    digit(date.getFullYear(), 4),
    digit(date.getMonth() + 1),
    digit(date.getDate())
  ];
  const hms = [
    digit(date.getHours()),
    digit(date.getMinutes()),
    digit(date.getSeconds())
  ];
  return format
    .replace(/yyyy/g, ymd[0])
    .replace(/MM/g, ymd[1])
    .replace(/dd/g, ymd[2])
    .replace(/HH/g, hms[0])
    .replace(/mm/g, hms[1])
    .replace(/ss/g, hms[2]);
}

/**
 * 时间语义化
 * @param time 时间
 * @param onlyDate 超过30天是否仅返回日期
 * @returns {string}
 */
export function timeAgo(time, onlyDate) {
  if (!time) {
    return '';
  }
  const arr = [[], []];
  const stamp = new Date().getTime() - new Date(time).getTime();
  // 30天以上返回具体日期
  if (stamp < 0 || stamp > 1000 * 60 * 60 * 24 * 31) {
    const date = new Date(time);
    arr[0][0] = digit(date.getFullYear(), 4);
    arr[0][1] = digit(date.getMonth() + 1);
    arr[0][2] = digit(date.getDate());
    // 是否输出时间
    if (!onlyDate) {
      arr[1][0] = digit(date.getHours());
      arr[1][1] = digit(date.getMinutes());
      arr[1][2] = digit(date.getSeconds());
    }
    return arr[0].join('-') + ' ' + arr[1].join(':');
  }
  if (stamp >= 1000 * 60 * 60 * 24) {
    return ((stamp / 1000 / 60 / 60 / 24) | 0) + '天前';
  }
  if (stamp >= 1000 * 60 * 60) {
    return ((stamp / 1000 / 60 / 60) | 0) + '小时前';
  }
  if (stamp >= 1000 * 60 * 3) {
    return ((stamp / 1000 / 60) | 0) + '分钟前';
  }
  return '刚刚';
}

/**
 * parentId形式数据转children形式
 * @param option
 * @returns {[]} 转换后的数据
 */
export function toTreeData(option) {
  // 兼容旧版
  if (Array.isArray(option)) {
    option = {
      data: arguments[0],
      idField: arguments[1],
      parentIdField: arguments[2],
      childrenField: arguments[3],
      parentId: arguments[4],
      addParentIds: arguments[5],
      parentIdsField: arguments[6],
      parentIds: arguments[7]
    };
  }
  const data = option.data;
  const idField = option.idField || option.idKey || 'id';
  const parentIdField = option.parentIdField || option.pidKey || 'parentId';
  const childrenField = option.childrenField || option.childKey || 'children';
  const parentIdIsNull =
    typeof option.parentId === 'undefined' && typeof option.pid === 'undefined';
  const parentId = parentIdIsNull ? [] : option.parentId ?? option.pid;
  const addParentIds = option.addParentIds ?? option.addPIds;
  const parentIdsField =
    option.parentIdsField || option.parentsKey || 'parentIds';
  const parentIds = option.parentIds;
  if (parentIdIsNull) {
    data.forEach((d) => {
      let flag = true;
      for (let i = 0; i < data.length; i++) {
        if (d[parentIdField] == data[i][idField]) {
          flag = false;
          break;
        }
      }
      if (flag) {
        parentId.push(d[parentIdField]);
      }
    });
  }

  const result = [];
  data.forEach((d) => {
    if (d[idField] == d[parentIdField]) {
      throw new Error(
        [
          'data error: {',
          idField + ': ',
          JSON.stringify(d[idField]),
          parentIdField + ': ',
          JSON.stringify(d[parentIdField]),
          '}'
        ].join('')
      );
    }
    const isArr = Array.isArray(parentId);
    const isParent = isArr
      ? parentId.includes(d[parentIdField])
      : d[parentIdField] == parentId;
    if (isParent) {
      const r = Object.assign({}, d);
      const children = toTreeData({
        data,
        idField,
        parentIdField,
        childrenField,
        parentId: d[idField],
        addParentIds,
        parentIdsField,
        parentIds: (parentIds ?? []).concat([d[idField]])
      });
      if (children.length > 0) {
        r[childrenField] = children;
      }
      if (addParentIds) {
        r[parentIdsField] = parentIds ?? [];
      }
      result.push(r);
    }
  });
  return result;
}

/**
 * 遍历children形式数据
 * @param data 需要遍历的数据
 * @param callback 回调
 * @param childrenField children字段名
 */
export function eachTreeData(data, callback, childrenField = 'children') {
  data.forEach((d) => {
    if (callback(d) !== false && d[childrenField]?.length) {
      eachTreeData(d[childrenField], callback, childrenField);
    }
  });
}

/**
 * 处理children形式数据
 * @param data 需要处理的数据
 * @param formatter 处理器
 * @param childrenField children字段名
 * @returns {[]} 处理后的数据
 */
export function formatTreeData(data, formatter, childrenField = 'children') {
  const result = [];
  data.forEach((d) => {
    const item = formatter(d);
    if (item !== false) {
      if (item[childrenField]?.length) {
        item[childrenField] = formatTreeData(
          item[childrenField],
          formatter,
          childrenField
        );
      }
      result.push(item);
    }
  });
  return result;
}

/**
 * 生成随机字符串
 * @param length 长度
 * @param radix 基数
 * @returns {string}
 */
export function uuid(length = 32, radix) {
  const num = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  let result = '';
  for (let i = 0; i < length; i++) {
    result += num.charAt(Math.floor(Math.random() * (radix || num.length)));
  }
  return result;
}

/**
 * 生成m到n的随机数
 * @param m 最小值, 包含
 * @param n 最大值, 不包含
 * @returns {number}
 */
export function random(m, n) {
  return Math.floor(Math.random() * (m - n) + n);
}

/**
 * 数字千分位
 * @param num 数字
 * @returns {string}
 */
export function formatNumber(num) {
  return String(num ?? '').replace(/(\d{1,3})(?=(\d{3})+(?:$|\.))/g, '$1,');
}

/**
 * html转义
 * @param html
 * @returns {string}
 */
export function escape(html) {
  return (html ?? '')
    .replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/'/g, '&#39;')
    .replace(/"/g, '&quot;');
}

/**
 * html转纯文本
 * @param html
 * @returns {string}
 */
export function htmlToText(html) {
  return (html ?? '').replace(/<[^>]+>/g, '');
}

/**
 * 深度克隆对象
 * @param obj 源对象
 * @return 克隆后的对象
 */
export function deepClone(obj) {
  let result;
  if (Array.isArray(obj)) {
    result = [];
  } else if (typeof obj === 'object') {
    result = {};
  } else {
    return obj;
  }
  Object.keys(obj).forEach((key) => {
    if (typeof obj[key] === 'object') {
      result[key] = deepClone(obj[key]);
    } else {
      result[key] = obj[key];
    }
  });
  return result;
}

/**
 * 赋值不改变原字段
 * @param target 目标对象
 * @param source 源对象
 * @param excludes 排除的字段
 */
export function assignObject(target, source, excludes) {
  Object.keys(target).forEach((key) => {
    if (!excludes?.includes(key)) {
      target[key] = source[key];
    }
  });
  return target;
}

/**
 * 判断是否是外链
 * @param url 地址
 * @returns {boolean}
 */
export function isUrl(url) {
  return !!(
    url &&
    (url.startsWith('http://') ||
      url.startsWith('https://') ||
      url.startsWith('//'))
  );
}

/**
 * 百度地图坐标转高德地图坐标
 * @param point 坐标
 * @returns {}
 */
export function bd09ToGcj02(point) {
  const x_pi = (3.14159265358979324 * 3000.0) / 180.0;
  const x = point.lng - 0.0065,
    y = point.lat - 0.006;
  const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
  const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
  return {
    lng: z * Math.cos(theta),
    lat: z * Math.sin(theta)
  };
}

/**
 * 高德地图坐标转百度地图坐标
 * @param point 坐标
 * @returns {}
 */
export function gcj02ToBd09(point) {
  const x_pi = (3.14159265358979324 * 3000.0) / 180.0;
  const x = point.lng,
    y = point.lat;
  const z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
  const theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
  return {
    lng: z * Math.cos(theta) + 0.0065,
    lat: z * Math.sin(theta) + 0.006
  };
}

/**
 * 播放音频
 * @param url 音频地址
 */
export function play(url) {
  return new Audio(url).play();
}

/**
 * 获取设备信息
 * @param key 自定义的agent
 * @returns
 */
export function device(key) {
  const agent = navigator.userAgent.toLowerCase();
  const result = { os: null, ie: false };

  // 获取版本号
  function getVersion(label) {
    const exp = new RegExp(label + '/([^\\s\\_\\-]+)');
    return (agent.match(exp) || [])[1] || false;
  }

  // 获取操作系统
  if (/windows/.test(agent)) {
    result.os = 'windows';
  } else if (/linux/.test(agent)) {
    result.os = 'linux';
  } else if (/iphone|ipod|ipad|ios/.test(agent)) {
    result.os = 'ios';
  } else if (/mac/.test(agent)) {
    result.os = 'mac';
  } else if (/android/.test(agent)) {
    result.os = 'android';
  }

  // 获取ie版本
  if (!!window.ActiveXObject || 'ActiveXObject' in window) {
    result.ie = (agent.match(/msie\s(\d+)/) || [])[1] || '11';
  }

  // 获取微信版本
  result.weixin = getVersion('micromessenger');

  // 获取自定义的agent
  if (key && !result[key]) {
    result[key] = getVersion(key);
  }

  // 是否移动设备
  result.android = /android/.test(agent);
  result.ios = result.os === 'ios';
  result.mobile = result.android || result.ios;
  return result;
}

/**
 * 倒计时
 * @param endTime 结束时间
 * @param serverTime 服务端当前时间
 * @param callback 回调
 * @returns {number} 定时器实例
 */
export function countdown(endTime, serverTime, callback) {
  if (typeof serverTime === 'function') {
    callback = serverTime;
    serverTime = undefined;
  }
  const end = new Date(endTime).getTime();
  const now = new Date(serverTime ?? new Date()).getTime();
  const count = end - now;
  const time = [
    Math.floor(count / (1000 * 60 * 60 * 24)), // 天
    Math.floor(count / (1000 * 60 * 60)) % 24, // 时
    Math.floor(count / (1000 * 60)) % 60, // 分
    Math.floor(count / 1000) % 60 // 秒
  ];
  const timer = setTimeout(() => {
    countdown(endTime, now + 1000, callback);
  }, 1000);
  callback && callback(count > 0 ? time : [0, 0, 0, 0], serverTime, timer);
  if (count <= 0) {
    clearTimeout(timer);
  }
  return timer;
}

/**
 * 浏览器全屏切换
 * @param el dom
 * @param fullscreen 是否全屏
 * @returns {boolean} 全屏状态
 */
export function toggleFullscreen(el, fullscreen) {
  if (typeof el === 'undefined' || el === null) {
    el = document.documentElement;
  }
  if (typeof fullscreen === 'undefined' || fullscreen === null) {
    fullscreen = !isFullscreen();
  }
  if (fullscreen) {
    const rfs =
      el.requestFullscreen ||
      el.requestFullScreen ||
      el.webkitRequestFullScreen ||
      el.mozRequestFullScreen ||
      el.msRequestFullScreen;
    if (rfs) {
      rfs.call(el);
    } else {
      throw new Error('您的浏览器不支持全屏模式');
    }
  } else {
    const cfs =
      document.exitFullscreen ||
      document.exitFullScreen ||
      document.webkitCancelFullScreen ||
      document.mozCancelFullScreen ||
      document.msExitFullscreen;
    cfs && cfs.call(document);
  }
  return fullscreen;
}

/**
 * 获取当前是否是全屏状态
 * @returns {boolean}
 */
export function isFullscreen() {
  return !!(
    document.fullscreenElement ||
    document.webkitFullscreenElement ||
    document.mozFullScreenElement ||
    document.msFullscreenElement
  );
}

/**
 * 获取屏幕宽度
 * @returns {number}
 */
export function screenWidth() {
  return document.documentElement.clientWidth || document.body.clientWidth;
}

/**
 * 获取屏幕高度
 * @returns {number}
 */
export function screenHeight() {
  return document.documentElement.clientHeight || document.body.clientHeight;
}

/**
 * 导出excel
 * @param XLSX XLSX对象
 * @param sheet 数组或sheet对象
 * @param sheetName 文件名称
 * @param type 文件格式
 */
export function exportSheet(XLSX, sheet, sheetName = 'sheet1', type = 'xlsx') {
  if (Array.isArray(sheet)) {
    sheet = XLSX.utils.aoa_to_sheet(sheet);
  }
  const workbook = {
    SheetNames: [sheetName],
    Sheets: {}
  };
  workbook.Sheets[sheetName] = sheet;
  XLSX.writeFile(workbook, sheetName + '.' + type);
}

/**
 * 排列组合二维数组
 * @param arrays 二维数组
 * @param separator 分隔符
 * @returns {[]}
 */
export function combineArrays(arrays, separator = '') {
  let result = [''];
  for (let i = 0; i < arrays.length; i++) {
    const temp = [];
    for (let j = 0; j < result.length; j++) {
      for (let k = 0; k < arrays[i].length; k++) {
        temp.push(result[j] + separator + arrays[i][k]);
      }
    }
    result = temp;
  }
  return result;
}
